﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Windows.Forms;
using System.Drawing;

namespace System
{
#pragma warning disable 1591
    public static class User32
    {
        public class SystemMenu : IDisposable
        {
            public class SystemMenuResult
            {
                public IntPtr Result { get; set; }
                public bool CallNextHook
                {
                    get;
                    set;
                }
            }
            public delegate SystemMenuResult MenuItemClickHandler(SystemMenu sender, int itemId);

            public Form Form
            {
                get;
                private set;
            }
            public IntPtr Menu
            {
                get;
                private set;
            }

            private IntPtr hookGetMessage = IntPtr.Zero;
            private Win32.HookProc hookGetMessageHandler;

            public SystemMenu(Form form)
            {
                Menu = IntPtr.Zero;
                Form = form;
                try
                {
                    Menu = User32.GetSystemMenu(form.Handle, false);
                    uint ProcId;
                    hookGetMessageHandler = new Win32.HookProc(MenuCommandHookProc);
                    hookGetMessage = Win32.SetWindowsHookEx(WH_GETMESSAGE, hookGetMessageHandler, IntPtr.Zero, User32.GetWindowThreadProcessId(form.Handle, out ProcId));
                }
                catch { }
            }
            Dictionary<int, MenuItemClickHandler> hookProcedures = new Dictionary<int, MenuItemClickHandler>();

            private IntPtr MenuCommandHookProc(int nCode, IntPtr wParam, IntPtr lParam)
            {
                if (nCode < 0)
                    return Win32.CallNextHookEx(hookGetMessage, nCode, wParam, lParam);

                MSG msg = (MSG)System.Runtime.InteropServices.Marshal.PtrToStructure(lParam, typeof(MSG));
                switch (msg.message)
                {
                    case WM_SYSCOMMAND:
                        if (hookProcedures.ContainsKey((int)msg.wParam))
                        {
                           var d = hookProcedures[(int)msg.wParam];
                           SystemMenuResult r = d.Invoke(this, (int)msg.wParam);
                            if(r.CallNextHook)
                                return Win32.CallNextHookEx(hookGetMessage, nCode, wParam, lParam);
                            else
                                return r.Result;
                        }
                        break;
                }
                return Win32.CallNextHookEx(hookGetMessage, nCode, wParam, lParam);
            }

            public bool InsertMenu(int Pos, int ID, String Item, MenuItemClickHandler proc)
            {
                if (hookProcedures.ContainsKey(ID))
                    throw new ArgumentException("Menu with ID " + ID + " is already added.");
                hookProcedures.Add(ID, new MenuItemClickHandler(proc));

                return (InsertMenu(Pos, (System.User32.ItemFlags.mfByPosition |
                System.User32.ItemFlags.mfString), ID, Item));
            }

            private bool InsertMenu(int Pos, System.User32.ItemFlags Flags, int ID, String Item)
            {
                return (User32.InsertMenuW(Menu, Pos, (Int32)Flags, ID, Item) == 0);
            }

            public bool AppendSeparator()
            {
                return AppendMenu(0, "", System.User32.ItemFlags.mfSeparator);
            }

            public bool AppendMenu(int ID, String Item, MenuItemClickHandler proc)
            {
                if (hookProcedures.ContainsKey(ID))
                    throw new ArgumentException("Menu with ID " + ID + " is already added.");
                hookProcedures.Add(ID, new MenuItemClickHandler(proc));

                return AppendMenu(ID, Item, System.User32.ItemFlags.mfString);
            }

            private bool AppendMenu(int ID, String Item, System.User32.ItemFlags Flags)
            {
                return (User32.AppendMenuW(Menu, (int)Flags, ID, Item) == 0);
            }

            public void Revert()
            {
                User32.GetSystemMenu(Form.Handle, true);
            }

            public void Dispose()
            {
                Revert();
                Win32.UnhookWindowsHookEx(hookGetMessage);
            }
        }


        [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
        public struct CWPSTRUCT
        {
            public IntPtr lparam;
            public IntPtr wparam;
            public int message;
            public IntPtr hwnd;
        }
        [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]

        public struct POINT
        {
            public int x;
            public int y;
        }

        [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
        public struct MSG
        {
            public int hwnd;
            public uint message;
            public uint wParam;
            public int lParam;
            public uint time;
            public POINT pt;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct BLENDFUNCTION
        {
            public byte BlendOp;
            public byte BlendFlags;
            public byte SourceConstantAlpha;
            public byte AlphaFormat;
        }

        public enum BlendOps : byte
        {
            AC_SRC_OVER = 0x00,
            AC_SRC_ALPHA = 0x01,
            AC_SRC_NO_PREMULT_ALPHA = 0x01,
            AC_SRC_NO_ALPHA = 0x02,
            AC_DST_NO_PREMULT_ALPHA = 0x10,
            AC_DST_NO_ALPHA = 0x20
        }

        public enum BlendFlags : uint
        {
            None = 0x00,
            ULW_COLORKEY = 0x01,
            ULW_ALPHA = 0x02,
            ULW_OPAQUE = 0x04
        }

        #region Consts
        public const int WH_GETMESSAGE = 3;
        public const int WH_CALLWNDPROC = 4;
        public const int myMenuItem = 0x1001;

        public const int WH_CALLWNDPROCRET = 12;
        public const int WM_MENUSELECT = 0x011F;

        public const int WM_SYSCOMMAND = 0x112;
        public const int SC_CONTEXTHELP = 0xf180;
        public const int SW_HIDE = 0;
        public const int SW_SHOWNORMAL = 1;
        public const int SW_NORMAL = 1;
        public const int SW_SHOWMINIMIZED = 2;
        public const int SW_SHOWMAXIMIZED = 3;
        public const int SW_MAXIMIZE = 3;
        public const int SW_SHOWNOACTIVATE = 4;
        public const int SW_SHOW = 5;
        public const int SW_MINIMIZE = 6;
        public const int SW_SHOWMINNOACTIVE = 7;
        public const int SW_SHOWNA = 8;
        public const int SW_RESTORE = 9;
        public const int SW_SHOWDEFAULT = 10;
        public const int SW_FORCEMINIMIZE = 11;
        public const int SW_MAX = 11;
        #endregion

        #region Menu
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        public static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
       
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        public static extern int GetMenuItemCount(IntPtr hMenu);
        
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        public static extern bool DrawMenuBar(IntPtr hWnd);
       
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        
        public static extern bool RemoveMenu(IntPtr hMenu, uint uPosition, uint uFlags);
        
        public const Int32 MF_BYPOSITION = 0x400;
        public const Int32 MF_REMOVE = 0x1000;

        [DllImport("USER32", EntryPoint = "AppendMenuW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
        public static extern int AppendMenuW(IntPtr MenuHandle, int Flags, int NewID, String Item);
        public static int AppendMenu(IntPtr MenuHandle, ItemFlags Flags, int NewID, String Item)
        {
            return AppendMenuW(MenuHandle, (int)Flags, NewID, Item);
        }

        [DllImport("USER32", EntryPoint = "InsertMenuW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
        public static extern int InsertMenuW(IntPtr hMenu, int Position, int Flags, int NewId, String Item);
       
        public static int InsertMenu(IntPtr hMenu, int Position, ItemFlags Flags, int NewId, String Item)
        {
            return InsertMenuW(hMenu, Position, (int)Flags, NewId, Item);
        }

        public enum ItemFlags
        { // The item ...
            mfUnchecked = 0x00000000, // ... is not checked
            mfString = 0x00000000, // ... contains a string as label
            mfDisabled = 0x00000002, // ... is disabled
            mfGrayed = 0x00000001, // ... is grayed
            mfChecked = 0x00000008, // ... is checked
            mfPopup = 0x00000010, // ... Is a popup menu. Pass the
            // menu handle of the popup
            // menu into the ID parameter.
            mfBarBreak = 0x00000020, // ... is a bar break
            mfBreak = 0x00000040, // ... is a break
            mfByPosition = 0x00000400, // ... is identified by the position
            mfByCommand = 0x00000000, // ... is identified by its ID
            mfSeparator = 0x00000800 // ... is a seperator (String and
            // ID parameters are ignored).
        }

        #endregion
        public static void RemoveSystemMenu(System.Windows.Forms.Form form)
        {
            IntPtr hMenu;
            int n;
            hMenu = GetSystemMenu(form.Handle, false);
            if (hMenu != IntPtr.Zero)
            {
                n = GetMenuItemCount(hMenu);
                while (n > 0)
                {
                    RemoveMenu(hMenu, (uint)(n - 1), MF_BYPOSITION | MF_REMOVE);
                    DrawMenuBar(form.Handle);
                    n = GetMenuItemCount(hMenu);
                }
            }
        }
        /// <summary>
        /// Provides access to function required to delete handle. This method is used internally
        /// and is not required to be called separately.
        /// </summary>
        /// <param name="hIcon">Pointer to icon handle.</param>
        /// <returns>N/A</returns>
        [DllImport("User32.dll")]
        public static extern int DestroyIcon(IntPtr hIcon);
        [DllImport("User32.dll")]
        public static extern int PostMessage(IntPtr hWnd, IntPtr wMsg, IntPtr wParam, IntPtr lParam);
        [DllImport("User32.dll")]
        public static extern IntPtr GetActiveWindow();

        [DllImport("user32.dll", SetLastError = true)]
        public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
        public static IntPtr FindWindow(string lpWindowName)
        {
            return FindWindow(null, lpWindowName);
        }
        [DllImport("user32.dll")]
        public static extern int ShowWindow(IntPtr hWnd, int nCmdShow);
        [DllImport("user32.dll")]
        public static extern int SetForegroundWindow(IntPtr hWnd);
        [DllImport("user32.dll")]
        public static extern IntPtr GetForegroundWindow();
        [DllImport("user32.dll")]
        public static extern Int32 GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
        public static Process GetProcessByHandle(IntPtr hwnd)
        {
            try
            {
                uint processID;
                GetWindowThreadProcessId(hwnd, out processID);
                return Process.GetProcessById((int)processID);
            }
            catch { return null; }
        }
        public static Process GetActiveWindowProcess()
        {
            try
            {
                uint processID;
                GetWindowThreadProcessId(GetForegroundWindow(), out processID);
                return Process.GetProcessById((int)processID);
            }
            catch { return null; }
        }
        [DllImport("user32.dll")]
        public static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab);
        [DllImport("user32.dll")]
        public static extern bool EnableWindow(IntPtr HWnd, bool Enabled);
        [DllImport("user32.dll")]
        public static extern void SetActiveWindow(IntPtr hWnd);
        [DllImport("user32.dll")]
        public static extern int IsIconic(IntPtr hWnd);

        [DllImport("User32.dll")]
        public static extern IntPtr GetDC(IntPtr hwnd);

        [DllImport("User32.dll")]
        public static extern void ReleaseDC(IntPtr dc);

        public static int ShowHelp()
        {
            return PostMessage(GetActiveWindow(), new IntPtr(WM_SYSCOMMAND), new IntPtr(SC_CONTEXTHELP), IntPtr.Zero);
        }
        [DllImport("user32.dll", SetLastError = true)]
        public static extern IntPtr SetParent(IntPtr child, IntPtr newParent);

        public struct KeyboardKeys
        {
            public const uint KEYEVENTF_KEYUP = 2;
            public const byte VK_CONTROL = 0x11;
            public const byte VK_G = 0x47;
            public const byte VK_g = 0x67;
            public const byte VK_ENTER = 0x0A;
        }
        [DllImport("user32.dll")]
        public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, uint dwExtraInfo);


        public const int  AW_HOR_POSITIVE  = 0x00000001;// 'Animates the window from left to right. This flag can be used with roll or slide animation.
        public const int  AW_HOR_NEGATIVE  = 0x00000002;// 'Animates the window from right to left. This flag can be used with roll or slide animation.
        public const int AW_VER_POSITIVE  = 0x00000004;//'Animates the window from top to bottom. This flag can be used with roll or slide animation.
        public const int AW_VER_NEGATIVE  = 0x00000008;//'Animates the window from bottom to top. This flag can be used with roll or slide animation.
        public const int AW_CENTER  = 0x00000010;// 'Makes the window appear to collapse inward if AW_HIDE is used or expand outward if the AW_HIDE is not used.
        public const int AW_HIDE  = 0x00010000;// 'Hides the window. By default, the window is shown.
        public const int AW_ACTIVATE  = 0x00020000;// 'Activates the window.
        public const int AW_SLIDE  = 0x00040000;// 'Uses slide animation. By default, roll animation is used.
        public const int AW_BLEND  = 0x00080000;//'Uses a fade effect. This flag can be used only if hwnd is a top-level 

        [DllImport("user32.dll")]
        public static extern void AnimateWindow(IntPtr handle, int time, int flags);

        public static void FadeIn(IntPtr handle, int time)
        {
            AnimateWindow(handle, time, AW_ACTIVATE | AW_BLEND);
        }
        public static void FadeIn(int time)
        {
            FadeIn(Process.GetCurrentProcess().MainWindowHandle, time);
        }
        public static void FadeOut(IntPtr handle, int time)
        {
            AnimateWindow(handle, time, AW_HIDE | AW_BLEND);
        }
        public static void FadeOut(int time)
        {
            FadeOut(Process.GetCurrentProcess().MainWindowHandle, time);
        }

        public enum WindowStyles
        {
            WS_EX_LAYERED = 0x00080000
        }

        public const int GWL_EXSTYLE = -20;
        public const int WS_EX_LAYERED = 0x80000;
        public const int LWA_ALPHA = 0x2;
        public const int LWA_COLORKEY = 0x1;

        [DllImport("user32.dll")]
        public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

        [DllImport("user32.dll")]
        public static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags);

        [DllImport("user32.dll", SetLastError = true)]
        public static extern int GetWindowLong(IntPtr hWnd, int nIndex);

        [DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
        public static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, ref Point pptDst,
            ref Size psize, IntPtr hdcSrc, ref Point pprSrc, Int32 crKey, ref BLENDFUNCTION pblend, BlendFlags dwFlags);

        [DllImport("gdi32.dll")]
        public static extern IntPtr CreateCompatibleDC(IntPtr hDC);

        [DllImport("user32.dll")]
        public static extern IntPtr GetWindowDC(IntPtr hWnd);

        [DllImport("gdi32.dll")]
        public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);

        [DllImport("gdi32.dll")]
        public static extern bool DeleteObject(IntPtr hObject);
       
        [DllImport("gdi32.dll")]
        public static extern bool DeleteDC(IntPtr hdc);

        [DllImport("user32.dll")]
        public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);

    }
#pragma warning restore 1591

}
